/*
@STI LICENSE
(C) 2006-2008 Universit�t Innsbruck.

This file is part of myOntology.

myOntologyis free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

myOntologyis distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with myOntology.  If not, see <http://www.gnu.org/licenses/>.

@STI LICENSE */
package controller.elementMapper;

import java.util.ArrayList;

import model.Vocabulary;

import org.openrdf.model.URI;
import org.openrdf.repository.RepositoryConnection;

import controller.HelperFunctions;
import controller.sesameDataConnector.AbstractDataConnector;

public class PropertyMapper extends Mapper {


	protected ArrayList<Exception> exceptionsDomain = null;
	protected ArrayList<Exception> exceptionsRange = null;

	protected void addExceptionDomain( Exception ex ) {
		
		if( this.exceptionsDomain==null )
			this.exceptionsDomain = new ArrayList<Exception>();
		this.exceptionOccured = true;
		this.exceptionsDomain.add( ex );
	}
	public ArrayList<Exception> getExceptionsDomain() {
		return this.exceptionsDomain;
	}

	protected void addExceptionRange( Exception ex ) {
		
		if( this.exceptionsRange==null )
			this.exceptionsRange = new ArrayList<Exception>();
		this.exceptionOccured = true;
		this.exceptionsRange.add( ex );
	}
	public ArrayList<Exception> getExceptionsRange() {
		return this.exceptionsRange;
	}
	
	public static URI determineType( ArrayList<URI> list ) {
		
		// entry = trimEntry(entry);
		
		URI ret = null;
		
		// ArrayList<URI> list = HelperFunctions.extractURIs(entry);
		
		if( list.size()==1 && (
				list.get(0).equals( Vocabulary.XMLSBOOLEAN ) || 
				list.get(0).equals( Vocabulary.XMLSDATETIME ) || 
				list.get(0).equals( Vocabulary.XMLSDOUBLE ) || 
				list.get(0).equals( Vocabulary.XMLSINTEGER ) ||
				list.get(0).equals( Vocabulary.XMLSSTRING ) ) ) {
			
			ret = Vocabulary.DATATYPEPROPERTY;
		}
		else {
			
			// determine if no datatype is contained:
			
			if( 
				!list.contains( Vocabulary.XMLSBOOLEAN ) &&
				!list.contains( Vocabulary.XMLSDATETIME ) &&
				!list.contains( Vocabulary.XMLSDOUBLE ) &&
				!list.contains( Vocabulary.XMLSINTEGER ) &&
				!list.contains( Vocabulary.XMLSSTRING ) ) {
				
				ret = Vocabulary.OBJECTPROPERTY;
			}			
		}
		
		
		return ret;
	}

	public static ArrayList<URI> selectAll() {
		
		ArrayList<URI> ret = AbstractDataConnector.selectAllUris( Vocabulary.DATATYPEPROPERTY );
		
		ArrayList<URI> tmp = AbstractDataConnector.selectAllUris( Vocabulary.OBJECTPROPERTY );
		if( tmp!=null ) {
			if( ret==null )
				ret = tmp;
			else
				ret.addAll( tmp );
		}
		
		return ret;
	}
	

	/**
	 * insert an DatatypeProperty into the sesame store
	 * @param data an DatatypeProperty object
	 * @param lang the language in which the DatatypeProperty should be inserted
	 * @return true if the DatatypeProperty was inserted
	 *//*
	protected static URI insert( RepositoryConnection con, AbstractPropertyContainer data, String lang) throws Exception {
		URI ret = null;

		try {
			
			URI id = Mapper.insert( con, data, lang);
			
			//insert all the data of the AbstractElement
			if ( id != null ) {
					

				if( data.getExample()!=null )
					con.add(id, Vocabulary.HASEXAMPLE, new LiteralImpl(data.getExample(), lang));
				
				// addURIs(con, id, Vocabulary.BELONGSTOONTOLOGYMODULE, data.getBelongsToOntologyModule() );
				// archive OntologyModules:
				VersioningMapper.archiveRemoveInsertSubjects(con, null, data.getBelongsToOntologyModule(), id, Vocabulary.BELONGSTOONTOLOGYMODULE );
				
				// addURIs(con, id, Vocabulary.DOMAIN, data.getDomain() );
				// archive domains:
				VersioningMapper.archiveRemoveInsertSubjects(con, null, data.getDomain(), id, Vocabulary.DOMAIN );
			
				ret = id;
			}
			else {
				con.rollback();
			}
		} catch( Exception e ) {
			con.rollback();
			throw e;
		}
		
		return ret;
	}
	*/
	
	
	// ######################################################################################
	// ######################################################################################


	public static ArrayList<URI> selectDomain( URI uri ) throws Exception {
		// Classes cannot be archived when changes were done in belongsToOm. So always the most actual version should be shown.
		
		// return generateURIObjects( uri, Vocabulary.DOMAIN, Vocabulary.CLASS );
		
		// is same as following (following is slightely faster):
		return AbstractDataConnector.selectObjects(VersioningMapper.getMostActualVersion(uri), Vocabulary.DOMAIN, Vocabulary.CLASS );
	}
	public boolean updateDomain( RepositoryConnection con, URI uri, String entry, String oldentry ) throws Exception {

		boolean ret = false;
		
		entry = trimEntry(entry);
		
		try {
	
			ArrayList<URI> actualEntry = selectDomain( uri );
			// ArrayList<URI> oldEntry = selectDomain( Mapper.selectVersionUri(uri, oldtimestamp) );
			// actualEntry and oldEntry will allways be the same as never p/v0 domain c would exist. Domains are archived with the class and not with the property!
			ArrayList<URI> oldEntry = HelperFunctions.extractURIs(oldentry);
			ArrayList<URI> newEntry = HelperFunctions.extractURIs(entry);

			if( isOneEntryDifferent( actualEntry, newEntry ) ) {
				Mapper.checkIfStoredInMeantime( 
						actualEntry,
						oldEntry );
				
				VersioningMapper.archiveRemoveInsertSubjects(
						con, 
						actualEntry, 
						newEntry, 
						uri, 
						Vocabulary.DOMAIN );
			}
			ret = true;
			
		}
		catch( Exception ex ) {
			this.addExceptionDomain( ex );
		}
		
		return ret;			
	}
	public boolean updateDomain( RepositoryConnection con, URI uri, ArrayList<URI> newEntry, String oldentry ) throws Exception {

		boolean ret = false;
		
		try {	
			ArrayList<URI> actualEntry = selectDomain( uri );
			// ArrayList<URI> oldEntry = selectDomain( Mapper.selectVersionUri(uri, oldtimestamp) );			
			ArrayList<URI> oldEntry = HelperFunctions.extractURIs(oldentry);

			if( isOneEntryDifferent( actualEntry, newEntry ) ) {
				Mapper.checkIfStoredInMeantime( 
						actualEntry,
						oldEntry );
				
				VersioningMapper.archiveRemoveInsertSubjects(
						con, 
						actualEntry, 
						newEntry, 
						uri, 
						Vocabulary.DOMAIN );
				
				// this will block other threads from overtaking this thread; do not know exectely why but it works...
				Mapper.checkIfStoredInMeantime( 
					actualEntry, 
					selectDomain( uri ) );
			}
			ret = true;
			
		}
		catch( Exception ex ) {
			this.addExceptionDomain( ex );
		}
		
		return ret;			
	}
	
	public static ArrayList<URI> selectBelongsToOM( URI uri ) throws Exception {
		
		return Mapper.selectBelongsToOM(uri);
	}
	public boolean updateBelongsToOM( RepositoryConnection con, URI uri, String entry, String timestamp ) throws Exception {

		return super.updateBelongsToOM(con, uri, entry, timestamp);	
	}
	public boolean updateBelongsToOM( RepositoryConnection con, URI uri, ArrayList<URI> entry, String timestamp ) throws Exception {

		return super.updateBelongsToOM(con, uri, entry, timestamp);	
	}
	/*
	public boolean insertBelongsToOM( RepositoryConnection con, URI uri, ArrayList<URI> entry ) throws Exception {

		return super.insertBelongsToOM(con, uri, entry);
	}
	public boolean insertBelongsToOM( RepositoryConnection con, URI uri, String newEntry ) throws Exception {

		return super.insertBelongsToOM(con, uri, newEntry);
	}
	*/
	
	
	

	public static ArrayList<URI> selectRange( URI uri ) throws Exception {
		
		return generateURIObjects( uri, Vocabulary.RANGE );
	}	
	public boolean updateRange( RepositoryConnection con, URI uri, String entry, String oldtimestamp ) throws Exception {

		boolean ret = false;
		
		entry = trimEntry(entry);
		
		try {
	
			ArrayList<URI> actualEntry = selectRange( uri );
			ArrayList<URI> oldEntry = selectRange( Mapper.selectVersionUri(uri, oldtimestamp) );
			ArrayList<URI> newEntry = HelperFunctions.extractURIs(entry);

			if( isOneEntryDifferent( actualEntry, newEntry ) ) {
				Mapper.checkIfStoredInMeantime( 
						actualEntry,
						oldEntry );
				
				VersioningMapper.archiveRemoveInsertURIs(
						con, 
						actualEntry, 
						newEntry, 
						uri, 
						Vocabulary.RANGE );
			}
			ret = true;
		}
		catch( Exception ex ) {
			this.addExceptionRange( ex );
		}
		
		return ret;	
	}
	public boolean updateRange( RepositoryConnection con, URI uri, ArrayList<URI> newEntry, String oldtimestamp ) throws Exception {

		boolean ret = false;
		
		try {
	
			ArrayList<URI> actualEntry = selectRange( uri );

			if( isOneEntryDifferent( actualEntry, newEntry ) ) {
				Mapper.checkIfStoredInMeantime( 
					actualEntry,
					selectRange( Mapper.selectVersionUri(uri, oldtimestamp) ) );
				
				VersioningMapper.archiveRemoveInsertURIs(
						con, 
						actualEntry, 
						newEntry, 
						uri, 
						Vocabulary.RANGE );
				
				// this will block other threads from overtaking this thread; do not know exectely why but it works...
				Mapper.checkIfStoredInMeantime( 
					actualEntry, 
					selectRange( uri ) );
			}
			ret = true;
		}
		catch( Exception ex ) {
			this.addExceptionRange( ex );
		}
		
		return ret;	
	}
	public boolean insertRange( RepositoryConnection con, URI uri, ArrayList<URI> newEntry ) throws Exception {

		boolean ret = false;
		
		try {
			
			AbstractDataConnector.addURIs(con, uri, Vocabulary.RANGE, newEntry );
			
			ret = true;
		}
		catch( Exception ex ) {
			this.addExceptionRange( ex );
		}
		
		return ret;	
	}	
	public boolean insertRange( RepositoryConnection con, URI uri, String entry ) throws Exception {

		boolean ret = false;
		
		try {
			
			ArrayList<URI> newEntry = HelperFunctions.extractURIs(entry);
			
			AbstractDataConnector.addURIs(con, uri, Vocabulary.RANGE, newEntry );
			
			ret = true;
		}
		catch( Exception ex ) {
			this.addExceptionRange( ex );
		}
		
		return ret;	
	}	
	

	public static ArrayList<URI> selectPartlyOverlapsWith( URI uri ) throws Exception {
		
		return selectPartlyOverlapsWith( uri, Vocabulary.PARTLYOVERLAPSWITHCLASS );
	}
	public boolean updatePartlyOverlapsWith( RepositoryConnection con, URI uri, ArrayList<URI> newEntry, String oldtimestamp ) throws Exception {

		return updatePartlyOverlapsWith(con, uri, Vocabulary.PARTLYOVERLAPSWITHCLASS, newEntry, oldtimestamp);
	}	
	

	public static ArrayList<URI> selectSameAs( URI uri ) throws Exception {
		
		return selectSameAs( uri, Vocabulary.SAMEPROPERTYAS );
	}
	public boolean updateSameAs( RepositoryConnection con, URI uri, ArrayList<URI> newEntry, String oldtimestamp ) throws Exception {

		return updateSameAs(con, uri, Vocabulary.SAMEPROPERTYAS, newEntry, oldtimestamp);
	}
	

	public static ArrayList<URI> selectNarrowerThan( URI uri ) throws Exception {
		
		return selectNarrowerThan( uri, Vocabulary.NARROWERTHANPROPERTIES );
	}
	public boolean updateNarrowerThan( RepositoryConnection con, URI uri, ArrayList<URI> newEntry, String oldtimestamp ) throws Exception {

		return updateNarrowerThan(con, uri, Vocabulary.NARROWERTHANPROPERTIES, newEntry, oldtimestamp);
	}
	public boolean insertNarrowerThan( RepositoryConnection con, URI uri, ArrayList<URI> newEntry ) throws Exception {

		return insertNarrowerThan(con, uri, Vocabulary.NARROWERTHANPROPERTIES, newEntry );
	}
	


	public static ArrayList<URI> selectBroaderThan( URI uri ) throws Exception {

		ArrayList<URI> ret = null;
		ArrayList<URI> dp = AbstractDataConnector.selectSubjects( Vocabulary.NARROWERTHANPROPERTIES, VersioningMapper.getMostActualVersion(uri), Vocabulary.DATATYPEPROPERTY );
		ArrayList<URI> op = AbstractDataConnector.selectSubjects( Vocabulary.NARROWERTHANPROPERTIES, VersioningMapper.getMostActualVersion(uri), Vocabulary.OBJECTPROPERTY );
		
		if( dp!=null ) {
			
			ret = dp;
			
			if( op!=null ) {
				ret.addAll( op );
			}
		}
		else {
			ret = op;
		}
		
		return ret;
	}
	public boolean updateBroaderThan( RepositoryConnection con, URI uri, ArrayList<URI> newEntry, String oldtimestamp ) throws Exception {

		boolean ret = false;
		
		try {			
			ArrayList<URI> actualEntry = selectBroaderThan( uri );
			ArrayList<URI> oldEntry = selectBroaderThan( Mapper.selectVersionUri(uri, oldtimestamp) );

			if( isOneEntryDifferent( actualEntry, newEntry ) ) {
				Mapper.checkIfStoredInMeantime( 
						actualEntry,
						oldEntry );
				
				VersioningMapper.archiveRemoveInsertObjects(
						con, 
						actualEntry, 
						newEntry, 
						uri, 
						Vocabulary.NARROWERTHANPROPERTIES );

				Mapper.checkIfStoredInMeantime( 
						actualEntry,
						selectBroaderThan( uri ) );
			}
			ret = true;	
		}
		catch( Exception ex ) {
			this.addExceptionBroaderThan( ex );
		}
		
		return ret;		
	}


}
